package net.avcompris.binding.dom.dirty;

import static com.avcompris.util.ExceptionUtils.nonNullArgument;
import net.avcompris.binding.BindConfiguration;
import net.avcompris.binding.dom.DomBinder;
import net.avcompris.binding.impl.AbstractBinder;
import net.avcompris.binding.sax.Unmarshaller;
import net.avcompris.binding.sax.UnmarshallerFactory;

import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.DOMBuilder;
import org.jdom2.output.SAXOutputter;
import org.w3c.dom.Node;

import com.google.common.base.Throwables;

public class DirtySAXJDomBinder extends AbstractBinder<Node> implements
		DomBinder {

	public DirtySAXJDomBinder(final UnmarshallerFactory unmarshallerFactory) {

		this.unmarshallerFactory = nonNullArgument(unmarshallerFactory,
				"unmarshallerFactory");
	}

	private final UnmarshallerFactory unmarshallerFactory;

	@Override
	protected final <T> T bindInterface(final BindConfiguration configuration,
			final Node node, final ClassLoader classLoader, final Class<T> clazz) {

		nonNullArgument(configuration, "configuration");
		nonNullArgument(node, "node");
		nonNullArgument(classLoader, "classLoader");
		nonNullArgument(clazz, "clazz");

		final Unmarshaller<T> unmarshaller = unmarshallerFactory
				.createUnmarshaller(configuration, classLoader, clazz);

		final Document document;
		final Element element;

		if (node instanceof org.w3c.dom.Document) {

			document = new DOMBuilder().build((org.w3c.dom.Document) node);
			element = null;

		} else if (node instanceof org.w3c.dom.Element) {

			document = null;
			element = new DOMBuilder().build((org.w3c.dom.Element) node);

		} else {

			throw new IllegalArgumentException("Node: "
					+ node.getClass().getName());
		}

		final SAXOutputter outputter = new SAXOutputter(unmarshaller);

		try {

			if (document != null) {

				outputter.output(document);

			} else {

				outputter.output(element);
			}

		} catch (final JDOMException e) {

			final Throwable cause = e.getCause();

			Throwables.propagateIfInstanceOf(cause, RuntimeException.class);

			throw new RuntimeException(e);
		}

		return unmarshaller.getResult();
	}
}
